import time
import copy
import sys

import jwt
from collections import Iterable

import requests

from app.db import db
from app.db.models.assets import Asset
from app.db.models.jobs import Job
from app.db.models.users import User
from app.config.settings import JWT_SECRET_KEY


class UserConfigReuse:
    """用户资产任务配置复用"""
    def __init__(self, username, template_url, targets: Iterable, interval,
                 graphql_api="https://www.scanv.com/graphql"):
        self.username = username
        self.template_url = template_url
        self.target = targets or []
        self.interval = interval
        self.graphql_api = graphql_api

    @property
    def user(self):
        return User.objects.filter(username=self.username).first() or {}

    @property
    def uid(self):
        if not self.user:
            print("用户不存在")
            sys.exit()
        return self.user.id

    @property
    def job(self):
        job = Job.objects.filter(targetUrl=self.template_url, uid=self.uid).first()
        if not job:
            print("模板资产不存在,配置失败")
            sys.exit()
        if not job.verification.isVerified:
            print(f"模板资产{job.targetUrl}未认证，配置失败")
            sys.exit()
        return job

    @property
    def asset(self):
        asset = Asset.objects.filter(id=self.job.assetId).first()
        if not asset:
            print("模板资产asset数据不存在, 配置失败")
            sys.exit()
        return asset

    def post_to_graph(self):
        base_job_record = self._get_job_update()
        base_asset_record = self._get_asset_update()

        if self.target:
            target_jobs = Job.objects.filter(targetUrl__in=self.target, uid=self.uid, enableMonitor=False)
        else:
            target_jobs = Job.objects.filter(uid=self.uid, enableMonitor=False)

        target_job_asset_list = [
            {
                "job": job,
                "asset": Asset.objects.with_id(job.assetId)
            } for job in target_jobs]
        print(f"查询匹配到目标资产有：{[item['job'].targetUrl for item in target_job_asset_list]}")
        success = 0
        for target_struct in target_job_asset_list:
            if not target_struct["job"].verification.isVerified:
                print(f"目标资产{target_struct['job'].targetUrl}未认证，无法配置")
                continue
            this_job_graphql = self._get_job_graphql(base_job_record, target_struct["job"])
            this_asset_graphql = self._get_asset_graphql(base_asset_record, target_struct["asset"])
            job_post_resp = self.request_graphql_api(this_job_graphql)
            asset_post_resp = self.request_graphql_api(this_asset_graphql)
            if job_post_resp.status_code == 200 and asset_post_resp.status_code == 200:
                print(f"资产周期配置从{self.template_url}复制到{target_struct['job'].targetUrl}成功, "
                      f"监测状态为：{base_job_record['enableMonitor']}")
                success += 1
            else:
                print(f"资产周期配置从{self.template_url}复制到{target_struct['job'].targetUrl}失败， "
                      f"job提交响应内容为：{job_post_resp.content.decode()}, "
                      f"asset提交响应内容为:{asset_post_resp.content.decode()}")
            time.sleep(self.interval)
        print(f"运行完成，共批量进行任务配置{success}个")

    def request_graphql_api(self, graphql_body):
        # token = jwt.encode(
        #     payload={
        #         'id': str(self.uid),
        #         'username': self.username,
        #         'entityType': 'user',
        #         'iat': int(time.time()),
        #         'exp': int(time.time()) + 60 * 60
        #     },
        #     key=JWT_SECRET_KEY,
        #     algorithm="HS256")
        token_doc = db.connection["default"]["cloudmonitor"].get_collection("tokens").find_one({"note": "运营后台"}) or {}
        token = token_doc.get("token")
        headers = {
            "cookie": f"from=; token={token}",
            "referer": "https://www.scanv.com/",
            "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
        }
        response = requests.post(url=self.graphql_api,
                                 json=graphql_body,
                                 headers=headers,
                                 verify=False)
        return response

    def _get_job_graphql(self, base_job_record, target_job):
        this_record = copy.deepcopy(base_job_record)
        # 非https资产 无法开启ssl监测,复制后的配置需要强制关闭
        if target_job.target.protocol != "https:":
            this_record["sslSettings"]["enable"] = False
        # 若漏洞监测为增量，传参仅传一个参数
        vul_collect = this_record["vulSettings"]["collect"]
        if vul_collect["type"] == "increment":
            this_record["vulSettings"]["collect"] = {"type": "increment"}

        this_record.update({
            "note": target_job.note,
            "target": {
                "protocol": target_job.target.protocol,
                "host": target_job.target.host,
                "hostname": target_job.target.hostname
            },
            "targetUrl": target_job.targetUrl,
            "sourceIp": target_job.sourceIp
        })
        this_graphql = {
            "variables": {
                "record": this_record,
                "filter": {
                    "_id": str(target_job.id)
                }
            },
            "query": "mutation ($filter: FilterUpdateOneJobInput!, $record: UpdateOneJobInput!, $immediateExec: Boolean) {\n  JobUpdateOne(filter: $filter, record: $record, immediateExec: $immediateExec) {\n    recordId\n    record {\n      vulSettings {\n        enable\n        enableSitePortraitTriggerMonitor\n        name\n        sourceIp\n        target\n        collect\n        alert\n        interval\n        __typename\n      }\n      sslSettings {\n        enable\n        enableSitePortraitTriggerMonitor\n        name\n        sourceIp\n        target\n        collect\n        alert\n        interval\n        __typename\n      }\n      securityEventSettings {\n        enable\n        enableSitePortraitTriggerMonitor\n        name\n        sourceIp\n        target\n        collect\n        alert\n        interval\n        __typename\n      }\n      contentSettings {\n        enable\n        enableSitePortraitTriggerMonitor\n        name\n        sourceIp\n        target\n        collect\n        alert\n        interval\n        __typename\n      }\n      httpSettings {\n        enable\n        enableSitePortraitTriggerMonitor\n        name\n        sourceIp\n        target\n        collect\n        alert\n        interval\n        __typename\n      }\n      pingSettings {\n        enable\n        enableSitePortraitTriggerMonitor\n        name\n        sourceIp\n        target\n        collect\n        alert\n        interval\n        __typename\n      }\n      __typename\n    }\n    __typename\n  }\n}\n"
        }
        return this_graphql

    def _get_asset_graphql(self, base_asset_record, target_job):
        this_record = copy.deepcopy(base_asset_record)
        this_graphql = {
            "variables": {
                "record": this_record,
                "filter": {
                    "_id": str(target_job.id)
                },
                "enableMonitor": self.job.enableMonitor
            },
            "query": "mutation ($filter: FilterUpdateOneAssetInput!, $record: UpdateOneAssetInput!, $immediateExec: Boolean, $enableMonitor: Boolean) {\n  AssetUpdateOne(\n    filter: $filter\n    record: $record\n    immediateExec: $immediateExec\n    enableMonitor: $enableMonitor\n  ) {\n    recordId\n    __typename\n  }\n}\n"
        }
        return this_graphql

    def _get_job_update(self) -> dict:
        """获取资产的主要复制配置，资产变动配置需要额外获取"""
        job_config = dict()

        # 资产列表参数
        job_config.update({
            "enableMonitor": self.job.enableMonitor,
            "enableAlert": self.job.enableAlert,
        })
        # 更新复制的配置参数
        job_settings_fields = ("vulSettings", "securityEventSettings", "sslSettings", "contentSettings")
        for field in job_settings_fields:
            job_config.update({
                field: self._rebuild_config(getattr(self.job, field).to_dict())
            })
        return job_config

    def _get_asset_update(self) -> dict:
        """获取资产变动的复制配置"""
        asset_config = dict()
        # 资产变动参数
        asset_settings = self._rebuild_config(self.asset.taskSettings.to_dict())
        asset_config.update({
            "taskSettings": asset_settings
        })
        return asset_config

    @staticmethod
    def _rebuild_config(config) -> dict:
        """删除配置中不更新的字段"""
        config.pop("taskId", None)
        config.pop("targetUrl", None)
        config.pop("target", None)
        return config


if __name__ == '__main__':
    from run import app
    handler = UserConfigReuse(
        username="fan5-25@qq.com",
        template_url="http://se.nicedefense.xyz",
        targets=("http://www.zongrongliang.com",)
    )
    handler.post_to_graph()
